//----------------------------------------------------------------------------
//
//  ThreadImpl.h
//
//	POSIX implementation of a cross-platform thread
//
//	Copyright (c) 2010, Greg Satz <satz@iranger.com>
//	All rights reserved.
//
//	SOFTWARE NOTICE AND LICENSE
//
//	This file is part of OpenZWave.
//
//	OpenZWave is free software: you can redistribute it and/or modify
//	it under the terms of the GNU Lesser General Public License as published
//	by the Free Software Foundation, either version 3 of the License,
//	or (at your option) any later version.
//
//	OpenZWave is distributed in the hope that it will be useful,
//	but WITHOUT ANY WARRANTY; without even the implied warranty of
//	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//	GNU Lesser General Public License for more details.
//
//	You should have received a copy of the GNU Lesser General Public License
//	along with OpenZWave.  If not, see <http://www.gnu.org/licenses/>.
//
//-----------------------------------------------------------------------------
#include <unistd.h>
#include "Defs.h"
#include "platform/Event.h"
#include "platform/Thread.h"
#include "ThreadImpl.h"

#ifdef DARWIN
#define pthread_yield pthread_yield_np
#endif

namespace OpenZWave
{
	namespace Internal
	{
		namespace Platform
		{

//-----------------------------------------------------------------------------
//	<ThreadImpl::ThreadImpl>
//	Constructor
//-----------------------------------------------------------------------------
			ThreadImpl::ThreadImpl(Thread* _owner, string const& _tname) :
					m_owner(_owner),
//	m_hThread( NULL ),  /* p_thread_t isn't a pointer in Linux, so can't do this */
					m_bIsRunning(false), m_name(_tname)
			{
			}

//-----------------------------------------------------------------------------
//	<ThreadImpl::~ThreadImpl>
//	Destructor
//-----------------------------------------------------------------------------
			ThreadImpl::~ThreadImpl()
			{
			}

//-----------------------------------------------------------------------------
//	<ThreadImpl::Start>
//	Start a function running on this thread
//-----------------------------------------------------------------------------
			bool ThreadImpl::Start(Thread::pfnThreadProc_t _pfnThreadProc, Event* _exitEvent, void* _pContext)
			{
				pthread_attr_t ta;

				pthread_attr_init(&ta);
				pthread_attr_setstacksize(&ta, 0);
				pthread_attr_setdetachstate(&ta, PTHREAD_CREATE_JOINABLE);

				// Create a thread to run the specified function
				m_pfnThreadProc = _pfnThreadProc;
				m_pContext = _pContext;
				m_exitEvent = _exitEvent;
				m_exitEvent->Reset();

				pthread_create(&m_hThread, &ta, ThreadImpl::ThreadProc, this);
				string threadname("OZW-");
				threadname.append(m_name);
#if !defined(__APPLE_CC__) && !defined(__FreeBSD__) && !defined(__NetBSD__)
				pthread_setname_np( m_hThread, threadname.c_str() );
#elif defined(__NetBSD__)
				if ( threadname.length() > PTHREAD_MAX_NAMELEN_NP )
				threadname.resize( PTHREAD_MAX_NAMELEN_NP );
				pthread_setname_np( m_hThread, "%s", (void *)threadname.c_str() );
#endif
				//fprintf(stderr, "thread %s starting %08x\n", m_name.c_str(), m_hThread);
				//fflush(stderr);

				pthread_attr_destroy(&ta);
				return true;
			}

//-----------------------------------------------------------------------------
//	<ThreadImpl::Terminate>
//	End this thread
//-----------------------------------------------------------------------------
			bool ThreadImpl::Terminate()
			{
				void* data = NULL;

				//fprintf(stderr, "thread %s stopping %08x running %d\n", m_name.c_str(), m_hThread, m_bIsRunning );
				//fflush(stderr);
				if (!m_bIsRunning)
				{
					return false;
				}

				// This will kill an app that doesn't catch and ignore it.
				// We need to find another way to interrupt select.
				// thread_kill( m_hThread, SIGALRM );

				//m_hThread = NULL;
				m_bIsRunning = false;
				pthread_cancel(m_hThread);
				pthread_join(m_hThread, &data);

				return true;
			}

//-----------------------------------------------------------------------------
//	<ThreadImpl::msSleep>
//	Cause thread to sleep for the specified number of milliseconds
//-----------------------------------------------------------------------------
			void ThreadImpl::Sleep(uint32 _millisecs)
			{
				usleep(_millisecs * 1000);
			}

//-----------------------------------------------------------------------------
//	<ThreadImpl::IsSignalled>
//	Test whether the thread has completed
//-----------------------------------------------------------------------------
			bool ThreadImpl::IsSignalled()
			{
				return !m_bIsRunning;
			}

//-----------------------------------------------------------------------------
//	<ThreadImpl::ThreadProc>
//	Entry point for running a function on this thread
//-----------------------------------------------------------------------------
			void *ThreadImpl::ThreadProc(void* _pArg)
			{
				ThreadImpl* pImpl = (ThreadImpl*) _pArg;
				//fprintf(stderr, "thread %s run begin %08x running %d\n", pImpl->m_name.c_str(), pImpl->m_hThread, pImpl->m_bIsRunning );
				//fflush(stderr);
				pImpl->Run();
				//fprintf(stderr, "thread %s run end %08x running %d\n", pImpl->m_name.c_str(), pImpl->m_hThread, pImpl->m_bIsRunning );
				//fflush(stderr);
				return 0;
			}

//-----------------------------------------------------------------------------
//	<ThreadImpl::Run>
//	Entry point for running a function on this thread
//-----------------------------------------------------------------------------
			void ThreadImpl::Run()
			{
				m_bIsRunning = true;
				m_pfnThreadProc(m_exitEvent, m_pContext);
				m_bIsRunning = false;

				// Let any watchers know that the thread has finished running
				m_owner->Notify();
			}
		} // namespace Platform
	} // namespace Internal
} // namespace OpenZWave
